package concurrecy.deadlock;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.*;

class Chopstick {
    // false 没有人使用， true 有人使用
    private boolean state = false;
    private static int count = 0;
    private final int id = count ++;

    public synchronized void take() throws InterruptedException {
        while(state)
            wait();
        state = true;
    }

    public synchronized void drop() {
        state = false;
        notifyAll();
    }

    @Override
    public String toString() {
        return "Chopstick" + id;
    }
}

class Philosopher implements Runnable {

    private Chopstick left;
    private Chopstick right;

    private static int count = 0;
    private final int id = count ++;
    private Random random = new Random();
    private final static int WAIT_TIME = 10;

    public Philosopher(Chopstick left, Chopstick right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public String toString() {
        return "Philosopher" + id;
    }

    protected void pause() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(random.nextInt(WAIT_TIME));
    }

    protected void loop() throws InterruptedException {
        System.out.println(this + " thinking ...");
        pause();
        System.out.println(this + " take right");
        right.take();
        System.out.println(this + " take left");
        left.take();
        System.out.println(this + " having ...");
        pause();
        System.out.println(this + " drop left");
        left.drop();
        System.out.println(this + " drop right");
        right.drop();
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                loop();
            }
        } catch(InterruptedException e) {
            System.out.println(this + " interrupted");
        }
    }
}

class Philosopher1 extends Philosopher {
    private BlockingQueue<Chopstick> queue;

    public Philosopher1(BlockingQueue<Chopstick> queue) {
        super(null, null);
        this.queue = queue;
    }

    @Override
    protected void loop() throws InterruptedException {
        System.out.println(this + " thinking ...");
        pause();
        System.out.println(this + " take");
        Chopstick left = queue.take();
        System.out.println(this + " take");
        Chopstick right = queue.take();
        System.out.println(this + " having ...");
        pause();
        System.out.println(this + " put");
        queue.add(left);
        System.out.println(this + " put");
        queue.add(right);
    }
}

/**
 * 哲学家就餐
 */
public class PhilosopherDemo {
    final static int SIZE = 5;

    public static void main(String[] args) throws IOException {
        Chopstick[] chopsticks = new Chopstick[SIZE];
        for (int i = 0; i < SIZE; i++) {
            chopsticks[i] = new Chopstick();
        }
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < SIZE; i++) {
            if(i == SIZE - 1) {
                service.execute(new Philosopher(chopsticks[0], chopsticks[i]));
            } else {
                service.execute(new Philosopher(chopsticks[i], chopsticks[(i + 1) % SIZE]));
            }
        }
        System.in.read();
        service.shutdownNow();
    }
}

class PhilosopherDemo1 {
    final static int SIZE = 5;
    public static void main(String[] args) throws IOException {
        BlockingQueue<Chopstick> chopsticks = new LinkedBlockingQueue<>();
        for (int i = 0; i < SIZE; i++) {
            chopsticks.add(new Chopstick());
        }
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < SIZE; i++) {
            service.execute(new Philosopher1(chopsticks));
        }
        System.in.read();
        service.shutdownNow();
    }
}
